#include "exceptions.h"
#include "log.h"

#include <chrono>
#include <iomanip>

extern "C"
{
#include <sys/types.h>
#include <unistd.h>
}

namespace Database
{

namespace Logging
{

using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
using std::chrono::microseconds;
using std::chrono::seconds;
using std::chrono::time_point;
using std::setfill;
using std::setw;


std::ofstream *LogWriter::ost_ = NULL;
std::mutex LogWriter::mutexWrite_;

LogWriter::LogWriter(const LogLevel& level)
{
    buffer_ << getTimeStr() << ' ' << getPidStr() << " [" << level << "] ";
}


LogWriter::LogWriter(const LogLevel& level, const char * const file, const unsigned int line)
{
    buffer_ << getTimeStr() << ' ' << getPidStr() << " (" << file << ':' << line << ") ["
            << level << "] ";
}


LogWriter::~LogWriter()
{
    std::lock_guard<std::mutex> lock(mutexWrite_);
    if(ost_)
        *ost_ << buffer_.str() << "\n" << std::flush;
}


const string LogWriter::getPidStr() const
{
    ostringstream o;

    o << "{" << ::getpid() << "}";
    return o.str();
}


const string LogWriter::getTimeStr() const
{
    const auto now = high_resolution_clock::now();
    const auto epoch = now.time_since_epoch();
    const auto usec = duration_cast<microseconds>(epoch).count()
                      - (duration_cast<seconds>(epoch).count() * 1000000);

    time_t tt = std::chrono::system_clock::to_time_t(now);
    struct tm tm = *::localtime(&tt);

    // 2013-12-27 12:34:56.123456

    char buffer[32];
    ::sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%06ld",
              tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
              tm.tm_hour, tm.tm_min, tm.tm_sec, usec);
    return string(buffer);
/*
    ostringstream o;
    o << tm.tm_year + 1900 << '-'
      << setw(2) << setfill('0') << tm.tm_mon + 1 << '-'
      << setw(2) << setfill('0') << tm.tm_mday << ' '
      << setw(2) << setfill('0') << tm.tm_hour << ':'
      << setw(2) << setfill('0') << tm.tm_min << ':'
      << setw(2) << setfill('0') << tm.tm_sec << '.'
      << setw(6) << setfill('0') << usec;

    return o.str();
*/
}


const bool LogWriter::open(const char * const fileName)
{
    if(!fileName)
        return false;

    close();
    ost_ = new ofstream(fileName, ofstream::out | ofstream::app);
    if(!ost_)
        throw EMemory();

    return ost_->good();
}


const bool LogWriter::close()
{
    if(ost_)
    {
        ost_->close();
        delete ost_;
        ost_ = NULL;
        return true;
    }

    return false;
}

}   // namespace Logging

}   // namespace Database
